/**
 * 
 */
package lastfmbaseball.core;

import java.util.*;

/**
 * @author DanBress
 * 
 */
public class RulesEngine
{
	private Map<AtBatResult, Integer> amountToAdvanceBy = new Hashtable<AtBatResult, Integer>();
	private List<Base> baseOrder = new ArrayList<Base>(5);

	public RulesEngine()
	{
		amountToAdvanceBy.put(AtBatResult.StrikeOut, 0);
		amountToAdvanceBy.put(AtBatResult.Single, 1);
		amountToAdvanceBy.put(AtBatResult.Double, 2);
		amountToAdvanceBy.put(AtBatResult.Triple, 3);
		amountToAdvanceBy.put(AtBatResult.Homerun, 4);
		amountToAdvanceBy.put(AtBatResult.Sacrifice, 1);
		amountToAdvanceBy.put(AtBatResult.Out, 0);

		baseOrder.add(Base.Batter);
		baseOrder.add(Base.First);
		baseOrder.add(Base.Second);
		baseOrder.add(Base.Third);
		baseOrder.add(Base.Home);
	}

	public void advanceRunners(AtBatResult result, List<BaseRunner> baseRunners)
	{
		if (result == AtBatResult.StrikeOut || result == AtBatResult.Out
				|| result == AtBatResult.Sacrifice)
		{
			BaseRunner batter = baseRunners.get(baseRunners.size() - 1);
			batter.setState(BaseRunnerState.Out);
		}

		for (int i = 0; i < baseRunners.size(); i++)
		{
			BaseRunner b = baseRunners.get(i);
			if (b.getState() != BaseRunnerState.Out)
			{
				int advanceBy = amountToAdvanceBy.get(result);
				int currentBase = baseOrder.indexOf(b.getBase());
				int nextBase = Math.min(currentBase + advanceBy, baseOrder
						.size() - 1);
				Base newBase = baseOrder.get(nextBase);
				b.setBase(newBase);
			}
		}
	}

	public AtBat handleBat(PlayerInGame pitcher, PlayerInGame batter,
			Umpire umpire, int pitcherRating, int batterRating,
			List<BaseRunner> baseRunners)
	{
		int difference = batterRating - pitcherRating;
		boolean isPinchHitter = batter.getState() == PlayerInGameState.PinchHitter;
		batter.setState(PlayerInGameState.InGameNotPitching);

		AtBatResult result;

		baseRunners.add(new BaseRunner(batter, Base.Batter,
				BaseRunnerState.Safe));

		if ((pitcherRating >= 8) && !isPinchHitter)
		{
			result = AtBatResult.StrikeOut;
		}
		else
		{
			if (difference == 0)
			{
				result = AtBatResult.Sacrifice;
			}
			if (difference == 1)
			{
				result = AtBatResult.Single;
			}
			else if (difference == 2)
			{
				result = AtBatResult.Double;
			}
			else if (difference == 3)
			{
				result = AtBatResult.Triple;
			}
			else if (batterRating == 10 || difference >= 4)
			{
				result = AtBatResult.Homerun;
			}
			else if (difference >= -2)
			{
				result = AtBatResult.Sacrifice;
			}
			else
			{
				result = AtBatResult.Out;
			}
		}

		advanceRunners(result, baseRunners);
		
		RatedSong pitcherSong = new RatedSong(umpire, pitcher, null, pitcherRating, false, false ,false);
		RatedSong batterSong = new RatedSong(umpire, batter, null, batterRating, false, false ,false);

		AtBat outCome = new AtBat(pitcher, batter, pitcherSong, batterSong, baseRunners, result, isPinchHitter);

		return outCome;
	}
}
